package com.zds.passwordbox.fragment;

import static android.app.Activity.RESULT_OK;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.Toast;

import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;

import com.zds.passwordbox.GlobalApplication;
import com.zds.passwordbox.MainActivity;
import com.zds.passwordbox.R;
import com.zds.passwordbox.common.Constant;
import com.zds.passwordbox.common.util.CryptoUtil;
import com.zds.passwordbox.common.util.FileUtil;
import com.zds.passwordbox.common.util.LoadingDialogUtil;
import com.zds.passwordbox.data.dao.AccountDao;
import com.zds.passwordbox.data.dao.PrivateKeyDao;
import com.zds.passwordbox.data.dao.domain.UserAccount;

import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.crypto.spec.SecretKeySpec;

public class MineFragment extends Fragment implements View.OnClickListener {

    private Toolbar toolbar;

    private final PrivateKeyDao keyDao;

    private final AccountDao accountDao;

    private ActivityResultLauncher<Intent> readFilePickerLauncher;
    private ActivityResultLauncher<Intent> writeFilePickerLauncher;

    private ActivityResultLauncher<Intent> readPlainFilePickerLauncher;

    private ActivityResultLauncher<Intent> writePlainFilePickerLauncher;

    public MineFragment(PrivateKeyDao keyDao, AccountDao accountDao) {
        this.keyDao = keyDao;
        this.accountDao = accountDao;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.mine_fragment, container, false);
        toolbar = requireActivity().findViewById(R.id.my_toolbar);
        ((AppCompatActivity) requireActivity()).setSupportActionBar(toolbar);
        setHasOptionsMenu(true);
        readFilePickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
                this::handleReadPickerResult
        );
        writeFilePickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
                this::handleWritePickerResult
        );
        readPlainFilePickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
                this::handlePlainReadPickerResult
        );
        writePlainFilePickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
                this::handlePlainWritePickerResult
        );

        RelativeLayout privateUserKey = view.findViewById(R.id.set_private_user_key);
        RelativeLayout exportAccount = view.findViewById(R.id.export_account_btn);
        RelativeLayout importAccount = view.findViewById(R.id.import_account_btn);
        RelativeLayout plainExport = view.findViewById(R.id.plain_export_btn);
        RelativeLayout plainImport = view.findViewById(R.id.plain_import_btn);
        privateUserKey.setOnClickListener(this);
        exportAccount.setOnClickListener(this);
        importAccount.setOnClickListener(this);
        plainExport.setOnClickListener(this);
        plainImport.setOnClickListener(this);
        return view;
    }

    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
        toolbar.setTitle(R.string.tab_mine);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        if (id == R.id.set_private_user_key) {
            new PrivateKeyDialog(keyDao).show(requireActivity().getSupportFragmentManager(), "PrivateKey");
        } else if (id == R.id.export_account_btn) {
            if (!requireActivity().getSharedPreferences(Constant.USER_SETTING_NAME,
                    Context.MODE_PRIVATE).getBoolean(Constant.USER_KEY, false)) {
                new TipDialog("请先设置个人密钥").show(requireActivity().getSupportFragmentManager(), "tip");
                return;
            }
            // 导出数据
            chooseFolderAndCreateFile(true, "encryptedAccount.csv");
        } else if (id == R.id.import_account_btn) {
            if (!requireActivity().getSharedPreferences(Constant.USER_SETTING_NAME,
                    Context.MODE_PRIVATE).getBoolean(Constant.USER_KEY, false)) {
                new TipDialog("请先设置个人密钥").show(requireActivity().getSupportFragmentManager(), "tip");
                return;
            }
            chooseFileAndRead(true);
        } else if (id == R.id.plain_export_btn) {
            if (!requireActivity().getSharedPreferences(Constant.USER_SETTING_NAME,
                    Context.MODE_PRIVATE).getBoolean(Constant.USER_KEY, false)) {
                new TipDialog("请先设置个人密钥").show(requireActivity().getSupportFragmentManager(), "tip");
                return;
            }
            // 导出数据
            chooseFolderAndCreateFile(false, "account.csv");
        } else if (id == R.id.plain_import_btn) {
            if (!requireActivity().getSharedPreferences(Constant.USER_SETTING_NAME,
                    Context.MODE_PRIVATE).getBoolean(Constant.USER_KEY, false)) {
                new TipDialog("请先设置个人密钥").show(requireActivity().getSupportFragmentManager(), "tip");
                return;
            }
            chooseFileAndRead(false);
        } else {
            showMyToast("暂未开发~~~", 3 * 1000);
        }
    }

    private void chooseFileAndRead(boolean encryptImport) {
        GlobalApplication application = (GlobalApplication) requireActivity().getApplication();
        application.setFromFilePicker(true);
        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("*/*");
//        intent.setType("text/csv");
        if (encryptImport) {
            readFilePickerLauncher.launch(intent);
        } else {
            readPlainFilePickerLauncher.launch(intent);
        }
    }

    private void chooseFolderAndCreateFile(boolean encryptExport, String fileName) {
        GlobalApplication application = (GlobalApplication) requireActivity().getApplication();
        application.setFromFilePicker(true);
        Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("text/csv");
        intent.putExtra(Intent.EXTRA_TITLE, fileName);
        if (encryptExport) {
            writeFilePickerLauncher.launch(intent);
        } else {
            writePlainFilePickerLauncher.launch(intent);
        }
    }

    private void handleReadPickerResult(ActivityResult result) {
        if (result.getResultCode() == RESULT_OK && result.getData() != null) {
            Uri uri = result.getData().getData();
            if (uri != null) {
                //显示加载对话框
                LoadingDialogUtil.showLoadingDialog(requireActivity(), "正在导入，请稍候...");
                try (InputStream is = requireActivity().getContentResolver().openInputStream(uri)) {
                    List<String[]> data;
                    // 加文件解密
                    String userKey = this.keyDao.getUserKey();
                    SecretKeySpec secretKeySpec = CryptoUtil.generateFixedKey(userKey);
                    byte[] bytes = CryptoUtil.decryptFile(is, secretKeySpec);
                    data = FileUtil.readCsvFile(new ByteArrayInputStream(bytes));

                    importAccount(data, true);
                    LoadingDialogUtil.hideLoadingDialog();
                    showMyToast("导入成功！", 2 * 1000);
                    // 刷新账号列表
                    if (getActivity() instanceof MainActivity) {
                        ((MainActivity) getActivity()).refreshAccountList();
                    }
                } catch (Exception e) {
                    LoadingDialogUtil.hideLoadingDialog();
                    showMyToast("读取文件失败！", 2 * 1000);
                }

            }
        } else {
            showMyToast("未正确选择文件！", 2 * 1000);
        }
    }

    private void handleWritePickerResult(ActivityResult result) {
        if (result.getResultCode() == RESULT_OK && result.getData() != null) {
            Uri uri = result.getData().getData();
            if (uri != null) {
                //显示加载对话框
                LoadingDialogUtil.showLoadingDialog(requireActivity(), "正在导出，请稍候...");
                try (OutputStream os = requireActivity().getContentResolver().openOutputStream(uri)) {
                    List<String[]> exportData = getExportData(true);
                    ByteArrayOutputStream byteOs = new ByteArrayOutputStream();
                    FileUtil.writeCsvFile(byteOs, exportData);
                    String userKey = this.keyDao.getUserKey();
                    SecretKeySpec secretKeySpec = CryptoUtil.generateFixedKey(userKey);
                    CryptoUtil.encryptStream(byteOs.toByteArray(), os, secretKeySpec);

                    showMyToast("保存路径：" + uri.getPath(), 5 * 1000);
                    LoadingDialogUtil.hideLoadingDialog();
                } catch (Exception e) {
                    LoadingDialogUtil.hideLoadingDialog();
                    showMyToast("保存文件失败！" + e.getMessage(), 2 * 1000);
                }
            }
        } else {
            showMyToast("未正确选择文件！", 2 * 1000);
        }
    }

    private void handlePlainReadPickerResult(ActivityResult result) {
        if (result.getResultCode() == RESULT_OK && result.getData() != null) {
            Uri uri = result.getData().getData();
            if (uri != null) {
                //显示加载对话框
                LoadingDialogUtil.showLoadingDialog(requireActivity(), "正在导入，请稍候...");
                try (InputStream is = requireActivity().getContentResolver().openInputStream(uri)) {
                    List<String[]> data;
                    data = FileUtil.readCsvFile(is);
                    importAccount(data, false);

                    LoadingDialogUtil.hideLoadingDialog();
                    showMyToast("导入成功！", 2 * 1000);
                } catch (Exception e) {
                    LoadingDialogUtil.hideLoadingDialog();
                    showMyToast("读取文件失败！", 2 * 1000);
                }

            }
        } else {
            showMyToast("未正确选择文件！", 2 * 1000);
        }
    }

    private void handlePlainWritePickerResult(ActivityResult result) {
        if (result.getResultCode() == RESULT_OK && result.getData() != null) {
            Uri uri = result.getData().getData();
            if (uri != null) {
                //显示加载对话框
                LoadingDialogUtil.showLoadingDialog(requireActivity(), "正在导出，请稍候...");
                try (OutputStream os = requireActivity().getContentResolver().openOutputStream(uri)) {
                    List<String[]> exportData = getExportData(false);

                    String[] title = {"typeText", "username", "secret", "type"};
                    exportData.add(0, title);
                    FileUtil.writeCsvFile(os, exportData);

                    showMyToast("保存路径：" + uri.getPath(), 5 * 1000);
                    LoadingDialogUtil.hideLoadingDialog();
                } catch (Exception e) {
                    LoadingDialogUtil.hideLoadingDialog();
                    showMyToast("保存文件失败！" + e.getMessage(), 2 * 1000);
                }
            }
        } else {
            showMyToast("未正确选择文件！", 2 * 1000);
        }
    }

    private void importAccount(List<String[]> data, boolean encrypt) {
        if (data == null || data.isEmpty()) {
            return;
        }
        if (!encrypt) {
            data.remove(0);
        }
        if (data.isEmpty()) {
            return;
        }
        for (String[] strArr : data) {
            UserAccount account = new UserAccount(strArr[0], strArr[1],
                    strArr[2], Integer.parseInt(strArr[3]));
            if (accountDao.accountExist(account.getTypeText(),
                    account.getUsername(), account.getType())) {
                continue;
            }
            if (encrypt) {
                accountDao.insert(account);
            } else {
                accountDao.encryptedInsert(account);
            }
        }
    }

    private List<String[]> getExportData(boolean encrypt) {
        List<String[]> data = new ArrayList<>();
        LinkedList<UserAccount> userAccounts = accountDao.queryList(null, null);
        for (UserAccount userAccount : userAccounts) {
            String[] row;
            if (!encrypt) {
                row = new String[]{userAccount.getTypeText(), userAccount.getUsername(),
                        decryptSecret(userAccount.getSecret()), userAccount.getType().toString()};
            } else {
                row = new String[]{userAccount.getTypeText(), userAccount.getUsername(),
                        userAccount.getSecret(), userAccount.getType().toString()};
            }
            data.add(row);
        }
        return data;
    }

    private String decryptSecret(String secret) {
        if (StringUtils.isBlank(secret)) {
            return "";
        }
        String userKey = this.keyDao.getUserKey();
        SecretKeySpec secretKeySpec = CryptoUtil.generateFixedKey(userKey);
        String decryptSecret;
        try {
            decryptSecret = CryptoUtil.decrypt(secretKeySpec, secret);
        } catch (Exception e) {
            showMyToast("解密密码出错!", 1000);
            return "";
        }
        return decryptSecret;

    }

    // data是显示内容，cnt是要显示的时间
    public void showMyToast(String data, int cnt) {
        final Toast toast = Toast.makeText(requireActivity(), data, Toast.LENGTH_LONG);
        //设置显示位置
        toast.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL, 0, 0);
        final Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                toast.show();
            }
        }, 0, 3000);
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                toast.cancel();
                timer.cancel();
            }
        }, cnt);
    }
}
